package dk.aiszone.lang.messer;

import japa.parser.ParseException;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.expr.Expression;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.stmt.BlockStmt;
import japa.parser.ast.stmt.ExpressionStmt;
import japa.parser.ast.stmt.ForStmt;
import japa.parser.ast.stmt.IfStmt;
import japa.parser.ast.stmt.ReturnStmt;
import japa.parser.ast.stmt.Statement;
import japa.parser.ast.stmt.SwitchEntryStmt;
import japa.parser.ast.stmt.SwitchStmt;
import japa.parser.ast.stmt.WhileStmt;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import dk.aiszone.lang.autosource.SourceFileParser;
import dk.aiszone.lang.autosource.generatorobjects.Method;
import dk.aiszone.lang.autosource.generatorobjects.helpers.StringBlock;
import dk.aiszone.lang.autosource.generatorobjects.interfaces.BodyContainer;
import dk.aiszone.lang.autosource.generatorobjects.interfaces.SimpleBlockObject;
import dk.aiszone.lang.messer.autosource.BlockStatement;
import dk.aiszone.lang.messer.autosource.ForStatement;
import dk.aiszone.lang.messer.autosource.IfStatement;
import dk.aiszone.lang.messer.autosource.LocalVariable;
import dk.aiszone.lang.messer.autosource.MesserMethod;
import dk.aiszone.lang.messer.autosource.ReturnStatement;
import dk.aiszone.lang.messer.autosource.SwitchStatement;
import dk.aiszone.lang.messer.autosource.VariableAssignment;
import dk.aiszone.lang.messer.autosource.WhileStatement;

/**
 * The <code>MesserSourceFileParser</code> is an extendsion of the <code>SourceFileParser</code>.<br>
 * The <code>MesserSourceFileParser</code> also converts the contents of of body into objects.<br>
 * These objects can later-on be used for modifying the source contained in the file.
 * 
 * @author Ais
 */
public class MesserSourceFileParser extends SourceFileParser {

    private Method currentMethodBeingHandled;

    /**
     * Constructor for the <code>MesserSourceFileParser</code>.
     * 
     * @param file
     *            The file to parse.
     * @throws ParseException
     */
    public MesserSourceFileParser(String file) throws ParseException {
        super(file);
    }

    @Override
    protected void handleBody(BlockStmt block, BodyContainer bodycontainer) {
        bodycontainer.appendToBody(new BlockStatement(handleStatements(block.getStmts())));
    }

    @Override
    protected Method createMethod(MethodDeclaration md) {
        Method method = new MesserMethod(md.getBeginLine(), md.getBeginColumn());
        handleMethodDeclaration(method, md);
        currentMethodBeingHandled = method;
        handleBody(md.getBody(), method);
        currentMethodBeingHandled = null;
        return method;
    }

    private SimpleBlockObject handleExpression(Expression expression) {
        if (expression == null) {
            return null;
        }

        if (expression instanceof VariableDeclarationExpr) {
            VariableDeclarationExpr expr = (VariableDeclarationExpr)expression;

            LocalVariable variable = new LocalVariable(createClass(expr.getType()));
            for (Iterator<VariableDeclarator> iterator = expr.getVars().iterator(); iterator.hasNext();) {
                VariableDeclarator declarator = iterator.next();
                variable.addVariable(new VariableAssignment(declarator.getId().getName(), handleExpression(declarator.getInit())));
            }
            return variable;
        }

        return new StringBlock(expression.toString());
    }

    private List<SimpleBlockObject> handleExpressions(List<Expression> exps) {
        if (exps == null) {
            return Collections.emptyList();
        }

        List<SimpleBlockObject> objects = new ArrayList<SimpleBlockObject>();
        for (Iterator<Expression> iterator = exps.iterator(); iterator.hasNext();) {
            objects.add(handleExpression(iterator.next()));
        }
        return objects;
    }

    private SimpleBlockObject handleStatement(Statement statement) {
        if (statement instanceof ExpressionStmt) {
            return handleExpression(((ExpressionStmt)statement).getExpression());
        } else if (statement instanceof ForStmt) {
            ForStmt forStmt = (ForStmt)statement;

            return new ForStatement(handleExpressions(forStmt.getInit()), handleExpression(forStmt.getCompare()), handleExpressions(forStmt.getUpdate()), (BlockStatement)handleStatement(forStmt.getBody()));
        } else if (statement instanceof IfStmt) {
            IfStmt ifStmt = (IfStmt)statement;
            return new IfStatement(ifStmt.getCondition().toString(), handleStatement(ifStmt.getThenStmt()), handleStatement(ifStmt.getElseStmt()), ifStmt.getBeginLine(), ifStmt.getBeginColumn());
        } else if (statement instanceof SwitchStmt) {
            SwitchStmt switchStmt = (SwitchStmt)statement;

            SwitchStatement _switch = new SwitchStatement(handleExpression(switchStmt.getSelector()), switchStmt.getBeginLine(), switchStmt.getBeginColumn());
            for (Iterator<SwitchEntryStmt> iterator = switchStmt.getEntries().iterator(); iterator.hasNext();) {
                SwitchEntryStmt entry = iterator.next();
                _switch.addEntry(handleExpression(entry.getLabel()), new BlockStatement(handleStatements(entry.getStmts())));
            }

            return _switch;
        } else if (statement instanceof WhileStmt) {
            WhileStmt whileStmt = (WhileStmt)statement;
            return new WhileStatement(handleExpression(whileStmt.getCondition()), (BlockStatement)handleStatement(whileStmt.getBody()), whileStmt.getBeginLine(), whileStmt.getBeginColumn());
        } else if (statement instanceof BlockStmt) {
            BlockStmt blockStmt = (BlockStmt)statement;
            return new BlockStatement(handleStatements(blockStmt.getStmts()));
        } else if (statement instanceof ReturnStmt) {
            ReturnStmt returnStmt = (ReturnStmt)statement;
            return new ReturnStatement(handleExpression(returnStmt.getExpr()), currentMethodBeingHandled.getReturnType(), returnStmt.getBeginLine(), returnStmt.getBeginColumn());
        }

        System.err.println("not handled statement: " + statement.getClass());
        return new StringBlock(statement.toString());
    }

    private List<SimpleBlockObject> handleStatements(List<Statement> stmts) {
        if (stmts == null) {
            return Collections.emptyList();
        }

        List<SimpleBlockObject> objects = new ArrayList<SimpleBlockObject>();
        for (Iterator<Statement> iterator = stmts.iterator(); iterator.hasNext();) {
            objects.add(handleStatement(iterator.next()));
        }
        return objects;
    }
}